package service

import (
	"bytes"
	"errors"
	"gin_gorm_oj/define"
	"gin_gorm_oj/help"
	"gin_gorm_oj/modles"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

// GetSubmitList
// @Tags 公共方法
// @Summary 提交详情
// @Param size query int false "size"
// @Param page query int false "page"
// @Param problem_identity query string false "problem_identity"
// @Param user_identity query string false "user_identity"
// @Param status query string false "status"
// @Success 200 {string} json "{"code":"200","data":""}"
// @Router /submit-list [get]
func GetSubmitList(c *gin.Context) {
	page, _ := strconv.Atoi(c.DefaultQuery("page", define.DefaultPage))
	size, _ := strconv.Atoi(c.DefaultQuery("size", define.DefaultSize))
	page = (page - 1) * size
	var count int64
	data := make([]modles.Submit, 0)
	problemIdentity := c.Query("problem_identity")
	userIdentity := c.Query("user_identity")
	status, _ := strconv.Atoi(c.Query("status"))
	tx := modles.GetSubmitList(problemIdentity, userIdentity, status)
	err := tx.Count(&count).Offset(page).Limit(size).Find(&data).Error
	if err != nil {
		c.JSON(200, gin.H{
			"code": -1,
			"msg":  "获取提交列表错误" + err.Error(),
		})
		return
	}
	c.JSON(200, gin.H{
		"code":  200,
		"data":  data,
		"count": count,
	})
}

// Submit
// @Tags 用户私有方法
// @Summary
// @Param authorization header string true "authorization"
// @Param problem_identity query string true "problem_identity"
// @Param code body string true "code"
// @Success 200 {string} json "{"code":"200","data":""}"
// @Router /submit [post]
func Submit(c *gin.Context) {
	problemIdentity := c.Query("problem_identity")
	code, err := ioutil.ReadAll(c.Request.Body)
	if err != nil {
		c.JSON(200, gin.H{
			"code": -1,
			"msg":  "Read Code" + err.Error(),
		})
		return
	}
	//代码保存
	path, err1 := help.CodeSave(code)
	if err1 != nil {
		c.JSON(200, gin.H{
			"code": -1,
			"msg":  "code save error" + err1.Error(),
		})
		return
	}
	//提交
	u, _ := c.Get("user")
	userClaims := u.(*help.UserClaims)
	sb := &modles.Submit{
		ProblemIdentity: problemIdentity,
		Identity:        help.GetUUID(),
		Path:            path,
		UserIdentity:    userClaims.Identity,
	}
	//判断
	pb := new(modles.Problem)
	err3 := modles.DB.Where("identity=?", problemIdentity).Preload("TestCases").First(pb).Error
	if err3 != nil {
		c.JSON(200, gin.H{
			"code": -1,
			"msg":  "get problem error" + err3.Error(),
		})
	}

	//答案错误的channel
	// 答案错误的channel
	WA := make(chan int)
	// 超内存的channel
	OOM := make(chan int)
	// 编译错误的channel
	CE := make(chan int)
	// 答案正确的channel
	AC := make(chan int)
	// 非法代码的channel
	EC := make(chan struct{})
	passCount := 0
	// 提示信息
	var msg string
	var lock sync.Mutex
	v, err4 := help.CheckGoCodeValid(path)
	if err4 != nil {
		c.JSON(http.StatusOK, gin.H{
			"code": -1,
			"msg":  "Code Check Error:" + err.Error(),
		})
		return
	}
	if !v {
		go func() {
			EC <- struct{}{}
		}()
	} else {
		for _, testCase := range pb.TestCases {
			testCase := testCase
			go func() {
				cmd := exec.Command("go", "run", path)
				var out, stderr bytes.Buffer
				cmd.Stderr = &stderr
				cmd.Stdout = &out
				stdinPipe, err := cmd.StdinPipe()
				if err != nil {
					log.Fatalln(err)
				}
				io.WriteString(stdinPipe, testCase.Input+"\n")

				var bm runtime.MemStats
				runtime.ReadMemStats(&bm)
				if err := cmd.Run(); err != nil {
					log.Println(err, stderr.String())
					if err.Error() == "exit status 2" {
						msg = stderr.String()
						CE <- 1
						return
					}
				}
				var em runtime.MemStats
				runtime.ReadMemStats(&em)

				// 答案错误strings.Trim(text, "\n")
				if strings.Trim(testCase.Output, "\n") != strings.Trim(out.String(), "\n") {
					WA <- 1
					return
				}
				// 运行超内存
				if em.Alloc/1024-(bm.Alloc/1024) > uint64(pb.MaxMem) {
					OOM <- 1
					return
				}
				lock.Lock()
				passCount++
				if passCount == len(pb.TestCases) {
					AC <- 1
				}
				lock.Unlock()
			}()
		}
	}
	select {
	case <-EC:
		msg = "无效代码"
		sb.Status = 6
	case <-WA:
		msg = "答案错误"
		sb.Status = 2
	case <-OOM:
		msg = "运行超内存"
		sb.Status = 4
	case <-CE:
		sb.Status = 5
	case <-AC:
		msg = "答案正确"
		sb.Status = 1
	case <-time.After(time.Millisecond * time.Duration(pb.MaxRuntime)):
		if passCount == len(pb.TestCases) {
			sb.Status = 1
			msg = "答案正确"
		} else {
			sb.Status = 3
			msg = "运行超时"
		}
	}
	if err = modles.DB.Transaction(func(tx *gorm.DB) error {
		err = tx.Create(sb).Error
		if err != nil {
			return errors.New("SubmitBasic Save Error:" + err.Error())
		}
		m := make(map[string]interface{})
		m["submit_num"] = gorm.Expr("submit_num + ?", 1)
		if sb.Status == 1 {
			m["pass_num"] = gorm.Expr("pass_num + ?", 1)
		}
		// 更新 user_basic
		err = tx.Model(new(modles.User)).Where("identity = ?", userClaims.Identity).Updates(m).Error
		if err != nil {
			return errors.New("UserBasic Modify Error:" + err.Error())
		}
		// 更新 problem_basic
		err = tx.Model(new(modles.Problem)).Where("identity = ?", problemIdentity).Updates(m).Error
		if err != nil {
			return errors.New("ProblemBasic Modify Error:" + err.Error())
		}
		return nil
	}); err != nil {
		c.JSON(http.StatusOK, gin.H{
			"code": -1,
			"msg":  "Submit Error:" + err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"code": 200,
		"data": map[string]interface{}{
			"status": sb.Status,
			"msg":    msg,
		},
	})

}
